class Compiler {
  constructor(vm) {
    this.el = vm.el;
    this.vm = vm;
    this.methods = vm.methods;
    this.compiler(this.el);
  }

  // 编译模板，处理元素节点和文本节点
  compiler(el) {
    // 获取 el 的所有子节点
    // 注意 childNodes 是子节点， childrenNodes
    const childNodes = el.childNodes;
    Array.from(childNodes).forEach((node) => {
      if (this.isTextNode(node)) {
        this.compilerText(node);
      } else if (this.isElementNode(node)) {
        this.compileElement(node);
      }

      // 如果节点中包含子节点，需要递归处理,因为 compiler 只能处理一层节点
      if (node.childNodes && node.childNodes.length) {
        this.compiler(node);
      }
    });
  }

  // 处理元素节点
  compileElement(node) {
    // 获取元素的属性
    // console.log(node.attributes);
    // 遍历元素属性
    Array.from(node.attributes).forEach((attr) => {
      const { name, value } = attr;
      // 这里目前只处理 v-text 和 v-model 两种指令
      if (this.isDirective(name)) {
        // 获取指令的名称,也就是函数的前缀
        const label = name.substr(2);
        this.updater(node, value, label);
      }
    });
  }

  updater(node, key, label) {
    // v-on 指令比较特殊，这里单独做处理
    if (label.startsWith("on")) {
      // 将 on:click 变成 click
      const eventType = label.replace("on:", "");
      const fn = this.methods[key];
      node.addEventListener(eventType, fn);
    } else {
      const updateFn = this[label + "Updater"];
      const value = this.vm[key];
      updateFn && updateFn.call(this, node, value, key);
    }
  }

  // 根据字符串创建元素节点
  createNode(string) {
    const tempNode = document.createElement("div");
    tempNode.innerHTML = string;
    return tempNode.firstChild;
  }

  // 删除 node 子节点
  removeNodeChild(node) {
    const child = node.childNodes;
    if (child.length) {
      child.forEach((item) => node.removeChild(item));
    }
  }

  // 处理 v-html 指令
  htmlUpdater(node, value, key) {
    // 先移除子节点再添加
    this.removeNodeChild(node);
    node.appendChild(this.createNode(value));
    new Watcher(this.vm, key, (newValue) => {
      this.removeNodeChild(node);
      node.appendChild(this.createNode(newValue));
    });
  }
  // 处理 v-text 指令
  textUpdater(node, value, key) {
    node.textContent = value;
    new Watcher(this.vm, key, (newValue) => {
      node.textContent = newValue;
    });
  }
  // 处理 v-model 指令
  modelUpdater(node, value, key) {
    node.value = value;
    new Watcher(this.vm, key, (newValue) => {
      node.value = newValue;
    });
    // 处理双向绑定
    node.addEventListener("input", () => {
      this.vm[key] = node.value;
    });
  }

  // 处理文本节点
  compilerText(node) {
    // 处理插值表达式 {{ abc }} 这样的内容
    const reg = /\{\{\s?(\S+)\s?\}\}/g;
    const text = node.textContent;
    if (reg.test(text)) {
      const key = RegExp.$1;
      // 这里只替换正则表单时能匹配到的
      node.textContent = text.replace(reg, this.vm[key]);
      new Watcher(this.vm, key, (newValue) => {
        node.textContent = text.replace(reg, newValue);
      });
    }
  }

  // 判断属性是否指令
  isDirective(attrName) {
    return attrName.startsWith("v-");
  }

  // 判断是否文本节点
  isTextNode(node) {
    return node.nodeType === 3;
  }

  // 判断是否元素节点
  isElementNode(node) {
    return node.nodeType === 1;
  }
}
